package com.iwdnb.gkgz.common.prediction;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;

import cn.hutool.core.date.DateUtil;
import com.iwdnb.gkgz.common.model.dto.StockDayData;
import com.iwdnb.gkgz.common.model.dto.StockPredictionData;
import com.iwdnb.gkgz.common.model.dto.prediction.StockPredictionParam;
import com.iwdnb.gkgz.common.utils.WorkDayUtils;
import org.springframework.stereotype.Component;

/**
 * 先以当日收盘价  S
 * 当日最高价  H
 * 当日最低价  L
 * 求出一个均价    J
 * J=(S x 2 + H + L) x 1/4
 * 有了当日均价 J 就能计算出明日的可能的最高价和最低价了：
 * 公式：
 * 求明日最高价：明高= J + H - L
 * 明日最低价：明低= J x 2 - H
 */
@Component
public class SecondPredictionStrategy implements PredictionStrategy {
    @Override
    public StockPredictionData predictionStock(StockDayData stockDayData,
        StockDayData yesterdayData, StockPredictionParam param) {
        StockPredictionData stockPredictionData = new StockPredictionData();
        stockPredictionData.setCode(stockDayData.getCode());
        //预测信息不准时,把预测信息设为当天的信息
        StockPredictionData prediction = new StockPredictionData();
        prediction.setCode(stockDayData.getCode());
        prediction.setDate(stockDayData.getDate());
        prediction.setMaxPrice(stockDayData.getTodayPrediction().getMaxPrice());
        prediction.setMinPrice(stockDayData.getTodayPrediction().getMinPrice());
        Date date = DateUtil.parseDate(stockDayData.getDate());
        Date nextDay = WorkDayUtils.getNextWorkDay(date);
        stockPredictionData.setDate(DateUtil.formatDate(nextDay));
        //(收盘-开盘)/2
        boolean upFlag = stockDayData.getOpenPrice().compareTo(stockDayData.getClosePrice()) <= 0;
        BigDecimal closePrice = stockDayData.getClosePrice();
        BigDecimal bigPrice = !upFlag ? stockDayData.getOpenPrice() : stockDayData.getClosePrice();
        BigDecimal smallPrice = !upFlag ? stockDayData.getClosePrice() : stockDayData.getOpenPrice();
        BigDecimal maxParam = getMaxParam(stockDayData, yesterdayData, prediction, bigPrice, smallPrice, param);
        BigDecimal minParam = getMinParam(stockDayData, yesterdayData, prediction, bigPrice, smallPrice, param);

        //第二日(周、月)上涨空间预测=收盘+变量
        BigDecimal maxPrice = closePrice.add(maxParam);
        stockPredictionData.setMaxPrice(maxPrice);
        //第二日(周、月)下跌空间预测=收盘-变量
        BigDecimal minPrice = closePrice.subtract(minParam);
        stockPredictionData.setMinPrice(minPrice);
        if (maxPrice.compareTo(stockPredictionData.getMinPrice()) <= 0) {
            minPrice = stockPredictionData.getMaxPrice().subtract(new BigDecimal(0.01)).setScale(2,
                RoundingMode.HALF_UP);
            //log.info("预测价格冲突,{},{}", maxPrice, stockPredictionData.getMinPrice());
        }
        stockPredictionData.setMinPrice(minPrice);
        return stockPredictionData;
    }

    private BigDecimal getMaxParam(StockDayData stockDayData, StockDayData yesterdayData,
        StockPredictionData prediction, BigDecimal bigPrice, BigDecimal smallPrice,
        StockPredictionParam param) {
        bigPrice = bigPrice.add(prediction.getMaxPrice()).add(stockDayData.getMaxPrice()).divide(new BigDecimal(3),
            BigDecimal.ROUND_HALF_DOWN);
        smallPrice = smallPrice.add(prediction.getMinPrice()).add(stockDayData.getMinPrice()).divide(new BigDecimal(3),
            BigDecimal.ROUND_HALF_DOWN);
        BigDecimal p = bigPrice.subtract(smallPrice)
            .multiply(param.getMaxRangeRate())
            .setScale(2, RoundingMode.HALF_DOWN);
        return p;
    }



    private BigDecimal getMinParam(StockDayData stockDayData, StockDayData yesterdayData,
        StockPredictionData prediction, BigDecimal bigPrice, BigDecimal smallPrice,
        StockPredictionParam param) {
        boolean upFlag = stockDayData.getOpenPrice().compareTo(stockDayData.getClosePrice()) <= 0;
        boolean yesterdayUpFlag = yesterdayData.getOpenPrice().compareTo(yesterdayData.getClosePrice()) <= 0;
        BigDecimal basic = new BigDecimal("1.000");
        BigDecimal closePrice = stockDayData.getClosePrice();
        BigDecimal yesterdayUpBalancePrice = yesterdayUpFlag ? yesterdayData.getMinPrice()
            : yesterdayData.getClosePrice();
        BigDecimal yesterdayDownBalancePrice = yesterdayUpFlag ? yesterdayData.getMaxPrice()
            : yesterdayData.getClosePrice();
        BigDecimal balanceUpPrice = upFlag ? stockDayData.getMinPrice() : closePrice;
        BigDecimal balanceDownPrice = upFlag ? stockDayData.getMaxPrice() : closePrice;
        //预测价格失准了,就是用当日最高价
        BigDecimal maxRange = prediction.getMaxPrice().subtract(stockDayData.getMaxPrice()).abs();
        BigDecimal predictionUpPrice = maxRange.compareTo(param.getMinRangeFirstThreshold()) < 0
            ? prediction.getMaxPrice()
            : stockDayData.getMaxPrice();
        BigDecimal minRange = prediction.getMinPrice().subtract(stockDayData.getMinPrice()).abs();
        BigDecimal predictionDownPrice = minRange.compareTo(param.getMinRangeFirstThreshold()) < 0
            ? prediction.getMinPrice()
            : stockDayData.getMinPrice();
        bigPrice = bigPrice.multiply(basic).add(closePrice)
            .add(yesterdayData.getClosePrice())
            .add(yesterdayData.getMaxPrice())
            .add(predictionUpPrice)
            .add(stockDayData.getMaxPrice())
            .add(balanceUpPrice)
            .add(yesterdayUpBalancePrice)
            .divide(param.getMinTotalHighDivideNum(), BigDecimal.ROUND_HALF_DOWN);
        smallPrice = smallPrice.multiply(basic).add(closePrice)
            .add(yesterdayData.getClosePrice())
            .add(yesterdayData.getMinPrice())
            .add(predictionDownPrice)
            .add(stockDayData.getMinPrice())
            .add(balanceDownPrice)
            .add(yesterdayDownBalancePrice)
            .divide(param.getMinTotalLowDivideNum(), BigDecimal.ROUND_HALF_UP);
        BigDecimal magic = bigPrice.subtract(smallPrice).divide(new BigDecimal("2"));
        BigDecimal rate = param.getMinRangeRate();
        if (minRange.compareTo(param.getMinRangeSecondThreshold()) > 0) {
            rate = param.getMinRangeSecondRate();
        } else if (minRange.compareTo(param.getMinRangeFirstThreshold()) > 0) {
            rate = param.getMinRangeFirstRate();
        }
        magic = magic.multiply(rate).setScale(2, RoundingMode.HALF_UP);
        return magic;
    }
}
